﻿using System;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Crm.Sdk;
using Microsoft.Crm.Sdk.Messages;
using Fine.CRM.Common;
using ProxyClasses;


namespace Fine.CRM.SolutionToolbox
{
    public class ExtensionSettingPlugin : Plugin
    {
        /// <summary>
        /// Defines setting names that can be added to the Extension Settings entity to alter
        /// plugin behavior. When a setting name definition is started with a period (.) then the 
        /// actual Extension Setting record name must start with the full plugin class name (e.g. MyNameSpace.MyTemplate.SettingName)
        /// </summary>
        class SettingNames
        {
            public const string SampleName = ".SampleName";
        }

        /// <summary>
        /// Defines cache keys used by the plugin when storing and retrieving data from the local context cache.
        /// </summary>
        class CacheItem
        {
            public const string SampleName = "SampleName";
        }
        
        public ExtensionSettingPlugin()
            : base(typeof(ExtensionSettingPlugin))
        {
            base.RegisteredEvents.Add(new Tuple<int, string, string, Action<Plugin.LocalPluginContext>>(PipelineStage.PreOp, MessageName.Create, fine_extensionsetting.GetLogicalName(), new Action<Plugin.LocalPluginContext>(this.ExecutePreExtensionSettingCreate)));
            base.RegisteredEvents.Add(new Tuple<int, string, string, Action<Plugin.LocalPluginContext>>(PipelineStage.PreOp, MessageName.Update, fine_extensionsetting.GetLogicalName(), new Action<Plugin.LocalPluginContext>(this.ExecutePreExtensionSettingUpdate)));
        }

        /// <summary>
        /// Encrypt the setting value on create if required.
        /// </summary>
        /// <param name="localContext"></param>
        protected void ExecutePreExtensionSettingCreate(Plugin.LocalPluginContext localContext)
        {
            if (localContext.Target.Contains(fine_extensionsetting.Properties.fine_encrypted) 
                && localContext.Target.Contains(fine_extensionsetting.Properties.fine_encrypted))
            {
                bool encrypted = localContext.Target.GetValue<bool>(fine_extensionsetting.Properties.fine_encrypted,false);
                
                if (encrypted)
                {
                    //encrypt the value in the message.
                    string value = localContext.Target.GetValue<string>(fine_extensionsetting.Properties.fine_value,null);
                    if (!string.IsNullOrEmpty(value))
                    {
                        localContext.Target.AddUpdateAttribute(fine_extensionsetting.Properties.fine_value, EncDec.Encrypt(value));
                    }
                }
            }
        }

        /// <summary>
        /// When an update occurs encrypt unencrypted values if required. If the values is already encrypted then prevent
        /// the user from changing the value back to an unecrypted state and also prevent the user from changing an existing 
        /// encrypted value.
        /// </summary>
        /// <param name="localContext"></param>
        protected void ExecutePreExtensionSettingUpdate(Plugin.LocalPluginContext localContext)
        {
            //if the encrypted flag has changed state then check the new value. If it is true
            //then encrypt the value. If false stop processing because an ecrypted value cannot 
            //be changed to unencrypted.
            if (localContext.Target.Contains(fine_extensionsetting.Properties.fine_encrypted))
            {
                if (localContext.Target.GetValue<bool>(fine_extensionsetting.Properties.fine_encrypted, false))
                {
                    if (localContext.Target.Contains(fine_extensionsetting.Properties.fine_value))
                    {
                        //encrypt the value in the update message.
                        string value = localContext.Target.GetValue<string>(fine_extensionsetting.Properties.fine_value, null);
                        if (!string.IsNullOrEmpty(value))
                        {
                            localContext.Target.AddUpdateAttribute(fine_extensionsetting.Properties.fine_value, EncDec.Encrypt(value));
                        }
                        else
                        {
                            //the value was not encrypted so remove the encrypted flag.
                            localContext.Target.RemoveAttribute(fine_extensionsetting.Properties.fine_encrypted);
                        }
                    }
                    else
                    {
                        //Get the current value from the database.
                        string value = localContext.OrganizationService.GetEntity(
                            localContext.Target.ToEntityReference(), 
                            fine_extensionsetting.Properties.fine_value)
                            .GetValue<string>(fine_extensionsetting.Properties.fine_value, null);
                                                
                        if (!string.IsNullOrEmpty(value))
                        {
                            localContext.Target.AddUpdateAttribute(fine_extensionsetting.Properties.fine_value, EncDec.Encrypt(value));
                        }
                        else
                        {
                            //the value was not encrypted so remove the encrypted flag.
                            localContext.Target.RemoveAttribute(fine_extensionsetting.Properties.fine_encrypted);
                        }
                    }
                }
                else
                {
                    //changing from encrypted = true to encrypted = false is not allowed.
                    string message = "A value that is encrypted cannot be changed to unencrypted. To change this setting to unenrypted, it must be deleted and re-entered.";
                    throw new InvalidPluginExecutionException(message);
                }
            }
            else if (localContext.Target.Contains(fine_extensionsetting.Properties.fine_value))
            {
                //query the database to see if the current value is encrypted.
                bool encrypted = localContext.OrganizationService.GetEntity(
                    localContext.Target.ToEntityReference(),
                    fine_extensionsetting.Properties.fine_encrypted).GetValue<bool>(fine_extensionsetting.Properties.fine_encrypted, false); ;

                if (encrypted)
                {
                    //encrypt the value in the update message.
                    string value = localContext.Target.GetValue<string>(fine_extensionsetting.Properties.fine_value,null);
                    if (!string.IsNullOrEmpty(value))
                    {
                        localContext.Target.AddUpdateAttribute(fine_extensionsetting.Properties.fine_value, EncDec.Encrypt(value));
                    }
                }
            }
        }
    }
}

